#include "engine.h"
#include "internal_types.h"
#include <set>
#include <fstream>
using namespace std;
#include "configreader.h"

namespace HW
{
using namespace Resource;
static set<TMaterial> lMaterials;

TMaterial CreateMaterial(void)
{
	AppLog ( "Creating material." );
	_i_Material* newMaterial = new _i_Material;

	newMaterial -> shader = NULL;
	newMaterial -> shadowShader = NULL;
	memset ( newMaterial->texture , NULL , HW_MAX_TEXTURE_SLOTS*sizeof(TTexture) );
	newMaterial -> clampMask = 0xFFFFFFFF;
	newMaterial -> color = Vec4 ( 1.0f );
	newMaterial -> specular = 1.0f;
	newMaterial -> gloss = 1.0f;
	newMaterial -> bumpscale = 1.0f;
	newMaterial -> collisionType = 0;
	newMaterial -> name = "";
	newMaterial -> boolMask = MASK_MATERIAL_DEPTH_MASK | MASK_MATERIAL_COLOR_MASK | MASK_MATERIAL_DEPTH_TEST | MASK_MATERIAL_CAST_SHADOW | MASK_MATERIAL_CULL_FACE;
	newMaterial -> zsort = 0;
	newMaterial -> blend = BLEND_NONE;

	lMaterials.insert( (TMaterial)(newMaterial) );
	return (TMaterial)(newMaterial);
}

TMaterial LoadMaterial(const_str name)
{
	string lPath = AbstractPath ( name );
	AppLog ( string("Loading material: \"" + lPath + "\"").c_str() );
	extern bool _i_FileExist ( const_str path );
	if ( !_i_FileExist ( lPath.c_str() ) )
	{
		AppLog ( string("Failed to load material: \"" + lPath + "\"").c_str() );
		return NULL;
	}

	ConfigFile matConfig;

	if ( !matConfig.ReadFile ( AbstractPath ( name ) ) )
		return NULL;

	_i_Material* newMaterial = new _i_Material;
	newMaterial -> shader = NULL;
	newMaterial -> shadowShader = NULL;
	memset ( newMaterial->texture , NULL , HW_MAX_TEXTURE_SLOTS*sizeof(TTexture) );
	newMaterial -> clampMask = 0xFFFFFFFF;
	newMaterial -> color = Vec4 ( 1.0f );
	newMaterial -> specular = 1.0f;
	newMaterial -> gloss = 1.0f;
	newMaterial -> bumpscale = 1.0f;
	newMaterial -> collisionType = 0;
	newMaterial -> name = "";
	newMaterial -> boolMask = MASK_MATERIAL_DEPTH_MASK | MASK_MATERIAL_COLOR_MASK | MASK_MATERIAL_DEPTH_TEST | MASK_MATERIAL_CAST_SHADOW | MASK_MATERIAL_CULL_FACE;
	newMaterial -> zsort = 0;
	newMaterial -> blend = BLEND_NONE;

	char* data;
	if ( data = matConfig.FindString ( "shader" ) )
	{
		string shaderPath = data;
		string vertShaderPath = shaderPath.substr ( 0 , shaderPath.find ( ',' ) );
		string fragShaderPath = shaderPath.substr ( shaderPath.find ( ',' ) + 1 );
		newMaterial -> shader = LoadShader ( AbstractPath ( vertShaderPath.c_str() ) , AbstractPath ( fragShaderPath.c_str() ) );
	}
	if ( data = matConfig.FindString ( "shadowshader" ) )
	{
		string shaderPath = data;
		string vertShaderPath = shaderPath.substr ( 0 , shaderPath.find ( ',' ) );
		string fragShaderPath = shaderPath.substr ( shaderPath.find ( ',' ) + 1 );
		newMaterial -> shadowShader = LoadShader ( AbstractPath ( vertShaderPath.c_str() ) , AbstractPath ( fragShaderPath.c_str() ) );
	}

	for ( int i = 0 ; i < HW_MAX_TEXTURE_SLOTS ; i ++ )
	{
		char ch[3];
		_itoa ( i , ch , 10 );
		string texStr = "texture";
		string clampStr = "clamp";
		texStr.append ( ch );
		clampStr.append ( ch );
		char* data;
		if ( data = matConfig.FindString ( texStr.c_str() ) )
			newMaterial->texture[i] = LoadTexture ( data );
		if ( data = matConfig.FindString ( clampStr.c_str() ) )
		{
			int clampData = atoi ( data );
			//TODO: take the clamping into account here.
		}
	}

	if ( data = matConfig.FindString ( "color" ) )
	{
		//TODO: take the color into account here.
	}

	if ( data = matConfig.FindString ( "specular" ) )
	{
		//TODO: take the specular into account here.
	}

	if ( data = matConfig.FindString ( "gloss" ) )
	{
		//TODO: take the gloss into account here.
	}

	if ( data = matConfig.FindString ( "bumpscale" ) )
	{
		//TODO: take the bumpscale into account here.
	}

	if ( data = matConfig.FindString ( "collisiontype" ) )
	{
		//TODO: take the bumpscale into account here.
	}

	//TODO: have to parse the file name here, without path and format, and save it here
	newMaterial -> name = lPath;

	if ( data = matConfig.FindString ( "depthmask" ) )
	{
		//TODO: take the depthmask into account here.
	}

	if ( data = matConfig.FindString ( "colormask" ) )
	{
		//TODO: take the colormask into account here.
	}

	if ( data = matConfig.FindString ( "depthtest" ) )
	{
		//TODO: take the depthtest into account here.
	}

	if ( data = matConfig.FindString ( "castshadows" ) )
	{
		//TODO: take the castshadows into account here.
	}
	
	if ( data = matConfig.FindString ( "invisible" ) )
	{
		//TODO: take the invisible into account here.
	}

	if ( data = matConfig.FindString ( "cullface" ) )
	{
		//TODO: take the zsort into account here.
	}
	
	if ( data = matConfig.FindString ( "overlay" ) )
	{
		//TODO: take the overlay into account here.
	}

	if ( data = matConfig.FindString ( "zsort" ) )
	{
		//TODO: take the zsort into account here.
	}
	
	if ( data = matConfig.FindString ( "blend" ) )
	{
		//TODO: take the blend into account here.
	}


	lMaterials.insert( (TMaterial)(newMaterial) );
	return (TMaterial)(newMaterial);
}

const_str MaterialName(TMaterial material)
{
	return ((_i_Material*)(material))->name.c_str();
}

void SetMaterialTexture(TMaterial material, TTexture texture, int stage)
{
	((_i_Material*)(material))->texture[stage] = texture;
}

TTexture GetMaterialTexture(TMaterial material, int stage)
{
	return ((_i_Material*)(material))->texture[stage];
}

void SetMaterialShader(TMaterial material, TShader shader, int shadow)
{
	if ( shadow )
		((_i_Material*)(material))->shadowShader = shader;
	else
		((_i_Material*)(material))->shader = shader;
}

TShader GetMaterialShader(TMaterial material, int shadow)
{
	if ( shadow )
		return ((_i_Material*)(material))->shadowShader;
	else
		return ((_i_Material*)(material))->shader;
}

void SetMaterialColor(TMaterial material, const TVec4 &color)
{
	((_i_Material*)(material))->color = color;
}

void SetMaterialCallback(TMaterial material, BP callback, int callbackID)
{
}

void SetMaterialKey(TMaterial material, const_str key, const_str value)
{
}

str GetMaterialKey(TMaterial material, const_str key)
{
	return "";
}

void FreeMaterial(TMaterial material)
{
	AppLog ( "Freeing material." );
	lMaterials.erase ( material );
	delete material;
}

void _i_UnInitMaterials ( void )
{
	while ( !lMaterials.empty() )
		FreeMaterial ( *lMaterials.begin() );
}

}